summaryrefslogtreecommitdiffstats
path: root/src/core/hle/kernel/k_handle_table.cpp
blob: e830ca46e19f56aa769e4867f7c5cdd0340112b0 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "core/hle/kernel/k_handle_table.h"

namespace Kernel {

KHandleTable::KHandleTable(KernelCore& kernel_) : kernel{kernel_} {}
KHandleTable::~KHandleTable() = default;

Result KHandleTable::Finalize() {
    // Get the table and clear our record of it.
    u16 saved_table_size = 0;
    {
        KScopedDisableDispatch dd(kernel);
        KScopedSpinLock lk(m_lock);

        std::swap(m_table_size, saved_table_size);
    }

    // Close and free all entries.
    for (size_t i = 0; i < saved_table_size; i++) {
        if (KAutoObject* obj = m_objects[i]; obj != nullptr) {
            obj->Close();
        }
    }

    return ResultSuccess;
}

bool KHandleTable::Remove(Handle handle) {
    // Don't allow removal of a pseudo-handle.
    if (Svc::IsPseudoHandle(handle)) {
        return false;
    }

    // Handles must not have reserved bits set.
    const auto handle_pack = HandlePack(handle);
    if (handle_pack.reserved != 0) {
        return false;
    }

    // Find the object and free the entry.
    KAutoObject* obj = nullptr;
    {
        KScopedDisableDispatch dd(kernel);
        KScopedSpinLock lk(m_lock);

        if (this->IsValidHandle(handle)) {
            const auto index = handle_pack.index;

            obj = m_objects[index];
            this->FreeEntry(index);
        } else {
            return false;
        }
    }

    // Close the object.
    kernel.UnregisterInUseObject(obj);
    obj->Close();
    return true;
}

Result KHandleTable::Add(Handle* out_handle, KAutoObject* obj) {
    KScopedDisableDispatch dd(kernel);
    KScopedSpinLock lk(m_lock);

    // Never exceed our capacity.
    R_UNLESS(m_count < m_table_size, ResultOutOfHandles);

    // Allocate entry, set output handle.
    {
        const auto linear_id = this->AllocateLinearId();
        const auto index = this->AllocateEntry();

        m_entry_infos[index].linear_id = linear_id;
        m_objects[index] = obj;

        obj->Open();

        *out_handle = EncodeHandle(static_cast<u16>(index), linear_id);
    }

    return ResultSuccess;
}

Result KHandleTable::Reserve(Handle* out_handle) {
    KScopedDisableDispatch dd(kernel);
    KScopedSpinLock lk(m_lock);

    // Never exceed our capacity.
    R_UNLESS(m_count < m_table_size, ResultOutOfHandles);

    *out_handle = EncodeHandle(static_cast<u16>(this->AllocateEntry()), this->AllocateLinearId());
    return ResultSuccess;
}

void KHandleTable::Unreserve(Handle handle) {
    KScopedDisableDispatch dd(kernel);
    KScopedSpinLock lk(m_lock);

    // Unpack the handle.
    const auto handle_pack = HandlePack(handle);
    const auto index = handle_pack.index;
    const auto linear_id = handle_pack.linear_id;
    const auto reserved = handle_pack.reserved;
    ASSERT(reserved == 0);
    ASSERT(linear_id != 0);

    if (index < m_table_size) {
        // NOTE: This code does not check the linear id.
        ASSERT(m_objects[index] == nullptr);
        this->FreeEntry(index);
    }
}

void KHandleTable::Register(Handle handle, KAutoObject* obj) {
    KScopedDisableDispatch dd(kernel);
    KScopedSpinLock lk(m_lock);

    // Unpack the handle.
    const auto handle_pack = HandlePack(handle);
    const auto index = handle_pack.index;
    const auto linear_id = handle_pack.linear_id;
    const auto reserved = handle_pack.reserved;
    ASSERT(reserved == 0);
    ASSERT(linear_id != 0);

    if (index < m_table_size) {
        // Set the entry.
        ASSERT(m_objects[index] == nullptr);

        m_entry_infos[index].linear_id = static_cast<u16>(linear_id);
        m_objects[index] = obj;

        obj->Open();
    }
}

} // namespace Kernel